use std::fmt::Debug;

use common_uu::IResult;


use crate::{a6_timestamp, u2_int};

use super::{a7_secs::SecsOneDay, a8_micros::MicrosOneSec};



#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, Default, PartialEq, Eq)]
pub struct TimeFT {
    /// seconds of whole day
    hms: SecsOneDay,
    //// micros of the insufficient second left
    io: MicrosOneSec,
}

impl TimeFT {
    /// i=milli
    /// o=micro
    pub fn from_micros_day_unsafe(micros_day: i64) -> Self {
        let (secs_day, micros) = u2_int::divide_remain_i64(micros_day, super::a8_micros::MICROS_PER_SEC as i64);
        

        Self {
            hms: SecsOneDay::from_secs_i64_unsafe(secs_day),
            io: MicrosOneSec::from_micros_unsafe(micros as i32),
        }
        
    }


    pub fn min() -> Self {
        Self {
            hms: SecsOneDay::min(),
            io: MicrosOneSec::min(),
        }
    }
    pub fn max() -> Self {
        Self {
            hms: SecsOneDay::max(),
            io: MicrosOneSec::max(),
        }
    }

    pub fn set_min(&mut self) {
        self.hms.set_min();
        self.io.set_min();
    }

}
    
impl TimeFT {
    pub fn now() -> Self {
        a6_timestamp::now_onlytime()
    }

    pub fn from_secs_micros(secs: i32, micros: i32) -> IResult<Self> {
        if secs < super::a7_secs::SECS_MIN || secs > super::a7_secs::SECS_MAX {
            let msg = format!("TimeFT::from_secs_micros error: secs must be in [{}, {}], secs={secs} is invalidate.", super::a7_secs::SECS_MIN, super::a7_secs::SECS_MAX);
            return Err(msg.as_str())?;
        }
        if micros < super::a8_micros::MICROS_MIN || micros > super::a8_micros::MICROS_MAX {
            let msg = format!("TimeFT::from_secs_micros error: micros must be in [{}, {}], micros={micros} is invalidate.", super::a8_micros::MICROS_MIN, super::a8_micros::MICROS_MAX);
            return Err(msg.as_str())?;
        }

        Ok(Self {
            hms: SecsOneDay::from_secs_unsafe(secs),
            io: MicrosOneSec::from_micros_unsafe(micros),
        })
    }

    pub fn from_secs_micros_unsafe(secs: i32, micros: i32) -> Self {
        let micros = if micros < super::a8_micros::MICROS_MIN {
            super::a8_micros::MICROS_MIN
        } else if micros > super::a8_micros::MICROS_MAX {
            super::a8_micros::MICROS_MAX
        } else {
            micros
        };

        Self {
            hms: SecsOneDay::from_secs_unsafe(secs),
            io: MicrosOneSec::from_micros_unsafe(micros),
        }
    }

    /// i=milli
    /// o=micro
    pub fn from_secsday_micros(secsday_micros: i64) -> Self {
        let (secsday, micros) = u2_int::divide_remain_i64(secsday_micros, super::a8_micros::MICROS_PER_SEC as i64);
        let secsday = secsday % super::a7_secs::SECS_PER_DAY as i64;
        Self {
            hms: SecsOneDay::from_secs_unsafe(secsday as i32),
            io: MicrosOneSec::from_micros_unsafe(micros as i32),
        }
        
    }


    /// for short: i=milli, o=micro;
    pub fn from_5_hmsio(hour: u8, minute: u8, second: u8, milli: u16, micro: u16) -> IResult<Self> {
        let secs = SecsOneDay::from_hms(hour, minute, second)?;
        let micros = MicrosOneSec::from_io(milli, micro)?;

        Ok(Self { hms: secs, io: micros })
    }
    
    /// please maker sure: hour∈[0, 23] && minute∈[0, 59] && second∈[0, 59] && milli∈[0, 999] && micro∈[0, 999];
    /// otherwise, this function is unsafe.
    /// for short: i=milli, o=micro;
    pub fn from_5_hmsio_unsafe(hour: u8, minute: u8, second: u8, milli: u16, micro: u16) -> Self {
        let secs = SecsOneDay::from_hms_unsafe(hour, minute, second);
        let micros = MicrosOneSec::from_io_unsafe(milli, micro);

        Self { hms: secs, io: micros }
    }
    
    /// i=milli
    /// o=micro
    pub fn from_4_hmsi(hour: u8, minute: u8, second: u8, milli: u16) -> IResult<Self> {
        let secs = SecsOneDay::from_hms(hour, minute, second)?;
        let micros = MicrosOneSec::from_i(milli)?;

        Ok(Self { hms: secs, io: micros })
    }
    
    /// please maker sure: hour∈[0, 23] && minute∈[0, 59] && second∈[0, 59] && milli∈[0, 999]; set micros = 0 defaultly.
    /// otherwise, this function is unsafe.
    /// for short: i=milli, o=micro;
    pub fn from_4_hmsi_unsafe(hour: u8, minute: u8, second: u8, milli: u16) -> Self {
        let secs = SecsOneDay::from_hms_unsafe(hour, minute, second);
        let micros = MicrosOneSec::from_i_unsafe(milli);

        Self { hms: secs, io: micros }
    }

    /// please maker sure: hour∈[0, 23] && minute∈[0, 59] && second∈[0, 59]; set io = 0(MicrosOneSec::min()) defaultly.
    /// otherwise, an error will be thrown.
    /// set io = 0(MicrosOneSec::min()) defaultly.
    pub fn from_3_hms(hour: u8, minute: u8, second: u8) -> IResult<Self> {
        let hms = SecsOneDay::from_hms(hour, minute, second)?;

        Ok(Self { hms, io: MicrosOneSec::min() })
    }
    
    /// please maker sure: hour∈[0, 23] && minute∈[0, 59] && second∈[0, 59]; set io = 0(MicrosOneSec::min()) defaultly.
    /// otherwise, this function is unsafe.
    pub fn from_3_hms_unsafe(hour: u8, minute: u8, second: u8) -> Self {
        let hms = SecsOneDay::from_hms_unsafe(hour, minute, second);

        Self { hms, io: MicrosOneSec::min() }
    }
    pub fn from_2_hm(hour: u8, minute: u8) -> IResult<Self> {
        let hms = SecsOneDay::from_hm(hour, minute)?;
        Ok(Self { hms, io: MicrosOneSec::min() })
    }
    /// please maker sure: hour∈[0, 23] && minute∈[0, 59]; set second=0, io = 0(MicrosOneSec::min()) defaultly.
    /// otherwise, this function is unsafe.
    pub fn from_2_hm_unsafe(hour: u8, minute: u8) -> Self {
        let secs = SecsOneDay::from_hm_unsafe(hour, minute);
        let micros = MicrosOneSec::min();

        Self { hms: secs, io: micros }
    }

    pub fn from_1_h(hour: u8) -> IResult<Self> {
        let secs = SecsOneDay::from_h(hour)?;
        let micros = MicrosOneSec::min();
        Ok(Self { hms: secs, io: micros })
    }
    /// please maker sure: hour∈[0, 23]; set minute=0, second=0, io = 0(MicrosOneSec::min()) defaultly.
    /// otherwise, this function is unsafe.
    pub fn from_1_h_unsafe(hour: u8) -> Self {
        let secs = SecsOneDay::from_h_unsafe(hour);
        let micros = MicrosOneSec::min();

        Self { hms: secs, io: micros }
    }
}



impl TimeFT {
    
    /// please maker sure: hour∈[0, 23] && minute∈[0, 59] && second∈[0, 59]; set io = 0(MicrosOneSec::min()) defaultly.
    /// otherwise, this function is unsafe.
    pub fn from_hmsi_friendly_unsafe(hhmmssiii: i32) -> Self {
        let (hhmmss, milli) = u2_int::divide_remain_i32(hhmmssiii, 1000);

        Self {
            hms: SecsOneDay::from_hms_friendly_unsafe(hhmmss),
            io: MicrosOneSec::from_i_unsafe(milli as u16),
        }

    }
    
    /// please maker sure: hour∈[0, 23] && minute∈[0, 59] && second∈[0, 59]; set io = 0(MicrosOneSec::min()) defaultly.
    /// otherwise, this function is unsafe.
    pub fn from_hms_io_friendly_unsafe(hhmmss: i32, iiiooo: i32) -> Self {
        Self {
            hms: SecsOneDay::from_hms_friendly_unsafe(hhmmss),
            io: MicrosOneSec::from_micros_unsafe(iiiooo),
        }

    }
}


impl TimeFT {
    pub fn hms_i32(&self) -> (i32, i32, i32) {
        self.hms.hms_i32()
    }
    pub fn hms(&self) -> (u8, u8, u8) {
        self.hms.hms()
    }

    /// for short: i=milli, o=micro
    pub fn io_i32(&self) -> (i32, i32) {
        self.io.io_i32()
    }
    
    /// for short: i=milli, o=micro
    pub fn io(&self) -> (u16, u16) {
        self.io.io()
    }
    
    /// for short: i=milli, o=micro
    pub fn hmsio(&self) -> (u8, u8, u8, u16, u16) {
        let (hour, minute, second) = self.hms.hms();
        let (milli, micro) = self.io.io();
        (hour, minute, second, milli, micro)
    }
    
    /// i32 value of io in friendly format
    /// i=milli
    /// o=micro
    pub fn io_friendly(&self) -> i32 {
        self.io.micros_inner()
    }
    /// i32 value of hmsi in friendly format
    /// i=milli
    /// o=micro
    pub fn hmsi_friendly(&self) -> i32 {
        self.hms.hms_friendly() * 1000 + self.io.milli() as i32
    }
    /// i64 value of hms in friendly format
    /// i=milli
    /// o=micro
    pub fn hmsio_friendly(&self) -> i64 {
        self.hms.hms_friendly() as i64 * 1000_000_i64 + self.io.micros_inner() as i64
    }
    pub fn hour(&self) -> u8 {
        self.hms.hour()
    }
    pub fn minute(&self) -> u8 {
        self.hms.minute()
    }
    pub fn second(&self) -> u8 {
        self.hms.second()
    }
    pub fn milli(&self) -> u16 {
        self.io.milli()
    }
    pub fn micro(&self) -> u16 {
        self.io.micro()
    }
    

}
impl TimeFT {
    pub fn to_print_hms(&self) -> String {
        self.hms.to_print()
    }
    pub fn to_print_hmsi(&self) -> String {
        let (hour, minute, second) = self.hms.hms();
        let (milli, _micro) = self.io.io();
        format!("{hour:0>2}:{minute:0>2}:{second:0>2}({milli:0>3})")
    }
    pub fn to_print_hmsio(&self) -> String {
        let (hour, minute, second) = self.hms.hms();
        let (milli, micro) = self.io.io();
        format!("{hour:0>2}:{minute:0>2}:{second:0>2}({milli:0>3}){micro:0>3}")
    }
    pub fn to_print_sio(&self) -> String {
        let second = self.second();
        let (milli, micro) = self.io.io();
        
        format!("{second:0>2}({milli:0>3}){micro:0>3}")
    }
}

impl TimeFT {
    // fn secs_from_inner(&self, tm_before: &Self) -> i32 {
    //     self.secs - tm_before.secs
    // }
    // fn micros_from_inner(&self, tm_before: &Self) -> i32 {
    //     self.micros - tm_before.micros
    // }

    pub fn ellapse_string(&self, tm_before: &Self) -> String {
        let mut secs = self.hms.seconds_from(&tm_before.hms);
        let mut micros = self.io.micros_from(&tm_before.io);
        if micros < 0 {
            secs -= 1;
            micros += super::a8_micros::MICROS_PER_SEC;
        }
        let millis = micros / super::a8_micros::MICROS_PER_MILLIS;
        let micros = micros % super::a8_micros::MICROS_PER_MILLIS;
        format!("{secs:0>2}({millis:0>3}){micros:0>3}")
    }

    pub fn micros_from(&self, tm_before: &Self) -> i64 {
        let secs = self.hms.seconds_from(&tm_before.hms) as i64;
        let micros = self.io.micros_from(&tm_before.io) as i64;
        secs * super::a8_micros::MICROS_PER_SEC as i64 + micros as i64
    }
    /// micro part is omitted by dividing 1000
    pub fn millis_from_i32(&self, tm_before: &Self) -> i32 {
        let secs = self.hms.seconds_from(&tm_before.hms);
        let millis = self.io.millis_from_i32(&tm_before.io);
        secs * super::a8_micros::MILLIS_PER_SEC + millis
    }

    pub fn millis_from_f64(&self, tm_before: &Self) -> f64 {
        let secs_1000 = self.hms.seconds_from(&tm_before.hms) * super::a8_micros::MILLIS_PER_SEC;
        let millis = self.io.millis_from_f64(&tm_before.io);
        secs_1000 as f64 + millis
    }

    pub fn seconds_from(&self, tm_before: &Self) -> f64 {
        let secs = self.hms.seconds_from(&tm_before.hms) as f64;
        let secs_left = self.io.seconds_from(&tm_before.io);
        secs + secs_left
    }

    pub fn minutes_from(&self, tm_before: &Self) -> f64 {
        self.seconds_from(tm_before) / super::a7_secs::SECS_PER_MINUTE as f64
    }

    pub fn hours_from(&self, tm_before: &Self) -> f64 {
        self.seconds_from(tm_before) / super::a7_secs::SECS_PER_HOUR as f64
    }
}


impl TimeFT {
    pub fn secs_of_day(&self) -> i32 {
        self.hms.secs_from_0_oclock()
    }
    pub fn micros_of_sec(&self) -> i32 {
        self.io.micros_inner()
    }

    pub fn micros_of_day(&self) -> i64 {
        self.hms.secs_from_0_oclock() as i64 * super::a8_micros::MICROS_PER_SEC as i64  + self.io.micros_inner() as i64
    }
    
    pub fn millis_of_day(&self) -> i32 {
        self.hms.secs_from_0_oclock() * super::a8_micros::MILLIS_PER_SEC  + self.io.milli() as i32
    }
    
    /// i32 value of hms in friendly format
    pub fn hms_friendly(&self) -> i32 {
        self.hms.hms_friendly()
    }
    /// i32 value of hms in friendly format
    pub fn hm_friendly(&self) -> i32 {
        self.hms.hm_friendly()
    }
}

impl Debug for TimeFT {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // f.debug_struct("TimeFT").field("display", &self.to_print_hmsio()).field("secs", &self.hms).field("micros", &self.io.micros_inner()).finish()
        write!(f, "{}", self.to_print_hmsio())
    }
}
impl std::fmt::Display for TimeFT {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.to_print_hmsio())
    }
}

impl TimeFT {
    pub fn to_le_bytes_array(&self) -> [u8; 8] {
        let buf_secs = self.hms.secs_from_0_oclock().to_le_bytes();
        let buf_micros = self.io.micros_inner().to_le_bytes();
        
        [buf_secs[0], buf_secs[1], buf_secs[2], buf_secs[3], buf_micros[0], buf_micros[1], buf_micros[2], buf_micros[3]]
    }
    pub fn to_le_bytes(&self) -> Vec<u8> {
        let buf_secs = self.hms.secs_from_0_oclock().to_le_bytes();
        let buf_micros = self.io.micros_inner().to_le_bytes();
        
        vec![buf_secs[0], buf_secs[1], buf_secs[2], buf_secs[3], buf_micros[0], buf_micros[1], buf_micros[2], buf_micros[3]]
    }
    pub fn from_le_bytes(bts: Vec<u8>) -> IResult<Self> {
        if bts.len() != 8 {
            return Err("TimeFT::from_le_bytes error1: bts.len() != 8")?;
        }
        let secs = i32::from_le_bytes([bts[0], bts[1], bts[2], bts[3]]);
        if secs < super::a7_secs::SECS_MIN || secs > super::a7_secs::SECS_MAX {
            return Err(format!("TimeFT::from_le_bytes error2: secs(of day) must be in [0, 86399], you input {secs} from [0..4) slice of {bts:?}."))?;
        }
        let micros = i32::from_le_bytes([bts[4], bts[5], bts[6], bts[7]]);
        if micros < super::a8_micros::MICROS_MIN || micros > super::a8_micros::MICROS_MAX {
            return Err(format!("TimeFT::from_le_bytes error3: micros(of second) must be in [0, 999_999], you input {micros} from [4..8) slice of {bts:?}."))?;
        }
        Ok(Self::from_secs_micros_unsafe(secs, micros))
    }
}

